////////////////////////////////////////////////////////////////////////////////////////
//
// JSimpleSim - A machine language simulator based on SimpSim by Anne-Gert Bultena.
//
// Copyright (C) 2008 Gerardo Camorlinga, Ryan Fitzgerald, and RJ Gordon
//
// This file is part of JSimpleSim.
//
// JSimpleSim is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// JSimpleSim is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with JSimpleSim.  If not, see <http://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////////////////////

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jsimplesim.model;

import java.util.Arrays;

import javax.swing.table.*;
import org.apache.log4j.Logger;
import javax.swing.event.TableModelListener;

/**
 *  A custom table model for our hex table component. Facilitates the storage
 *  of hex digits into bytes, so that they can be easily serialized. Also handles
 *  the storage and retrieval of our data.
 * @author f08maroon
 */
public class PcHexTableModel extends AbstractTableModel {

    /**
	 * 
	 */
	private static final long serialVersionUID = -8989617709509838433L;
	static Logger logger = Logger.getLogger(MemHexTableModel.class);
    private static final String HEX = "0123456789ABCDEF";
    private static final int CHARACTERS_PER_ROW = 1;
    private static String[] columnNames = {
        ""
    };
    private byte[] buffer;

    /** 
     * Default Constructor. Initializes the model to all zero's just like
     * the SimpleSim version.
     */
    public PcHexTableModel() {

        buffer = new byte[]{0};
        this.resetBuffer(buffer);

    }

    /**
     * Sets the data model to a new model. Used when loading binary files,
     * or clearing the contents of the view.
     * @param buffer An array of bytes with the data we want. Has to be 256 in length.
     * 
     */
    public void resetBuffer(byte[] buffer) {
        this.buffer = buffer;
        fireTableDataChanged();
    }
    

    @Override
	public String toString() {
		return "PcHexTableModel [buffer=" + Arrays.toString(buffer) + "]";
	}

	/**
     * Getter method for our data.
     * @return An array of bytes containing our data.
     */
    public byte[] getBuffer() {
        return this.buffer;
    }

    /**
     * Getter method for the number of columns in our model.
     * @return The number of columns.
     */
    public int getColumnCount() {
        return columnNames.length;
    }

    /**
     * Getter method for the number of rows in our model.
     * @return The number of rows.
     */
    public int getRowCount() {
        if (buffer == null) {
            return 0;
        } else {
            return (int) Math.ceil(buffer.length / (float) CHARACTERS_PER_ROW);
        }
    }

    /**
     * Getter method for a specific column of our model.
     * 
     * @param col The index of the column in the model.
     * @return The name of the column.
     */
    @Override
    public String getColumnName(int col) {
        return columnNames[col];
    }

    /**
     * Getter method for a data element in the model.
     * 
     * @param row The row index of the element.
     * @param col The column index of the element.
     * @return The element in the form of an Object. It's actually a String.
     */
    public Object getValueAt(int row, int col) {

        //fireTableCellUpdated(row, col);

        //  Hex Data Columns

        int index = (row * CHARACTERS_PER_ROW);


        if (index < buffer.length) {
            return byteToHex(buffer[index]);
        } else {
            return null;
        }
    }

    /**
     * Getter method that gets the type of a column.
     * @param c Not used for anything
     * @return All columns are of type String so returns String.class.
     */
    @Override
    public Class<String> getColumnClass(int c) {
        return String.class;
    }

    /**
     * Checks to see whether a cell is editable or not. Depending on whether
     * the cell is under the address column or not.
     * @param row The row index.
     * @param col The column index.
     * @return Whether or not cell is editable.
     */
    @Override
    public boolean isCellEditable(int row, int col) {
        if (col == 0) {
            return true;
        }
        int index = (row * CHARACTERS_PER_ROW);

        if (index < buffer.length) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Updates the model with changes to the view.
     * @param value The updated value of the view.
     * 
     * @param row The index of the row.
     * @param col The index of the column.
     */
    @Override
    public void setValueAt(Object value, int row, int col) {
        String hexString = value.toString();
        logger.info("hexString = " + hexString);

        if (hexString.length() > 0) {
            byte hexByte = (byte) Integer.parseInt(hexString, 16);
            logger.info("hexByte = " + hexByte);
            int index = (row * CHARACTERS_PER_ROW);

            logger.info("index = " + index);
            logger.info("buffer length = " + buffer.length);
            logger.info("column count = " + getColumnCount());
            buffer[index] = hexByte;

            //fireTableCellUpdated(row, col);
            fireTableDataChanged();
            logger.debug("PC table cell has been updated");
        }
    }

    @Override
    public void addTableModelListener(TableModelListener pc) {
        super.addTableModelListener(pc);
        fireTableDataChanged();
    }

    /**
     * Changes a byte into a string.
     * @param value The byte to convert.
     * @return The string representation of the byte.
     */
    private String byteToHex(int value) {
        int hi = (value & 0xF0) >> 4;
        int lo = (value & 0x0F);
        return "" + HEX.charAt(hi) + HEX.charAt(lo);
    }

    /**
     * Changes an int into a string.
     * @param value The int to convert.
     * @return The string representatin of the int.
     */
    @SuppressWarnings("unused")
	private String intToHex(int value) {
        int hi = (value & 0xFF00) >> 8;
        int lo = (value & 0x00FF);
        return byteToHex(hi) + byteToHex(lo);
    }
}


